<?php
/*
	This file is part of PGallery; a FrogCMS plugin.

	PGallery is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	PGallery is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with PGallery.  If not, see <http://www.gnu.org/licenses/>.
*/


#require "models/ImageLib.php";

// Include actual libs that do the work
require "models/LibGd.php";
require "models/LibMagick.php";


class PGalleryController extends PluginController{
	
	
	// Base upload and publically-accessible directory
	var $base;
	// Path separator
	var $sep;
	// Image processing library
	var $imglib;
	// Error messages
	var $err;
    // Settings for the plugin
    var $settings;
	
	
    public function __construct(){
		
        $this->setLayout('backend');
        $this->assignToLayout('sidebar', new View('../../plugins/pgallery/views/sidebar', array(
			'params' => Dispatcher::getParams()
		)));
		
		// Set base directory
		$this->base = FROG_ROOT;
		$this->base = preg_replace('/'.ADMIN_DIR.'[\/|\\\]/', '', $this->base);
		$this->base = preg_replace('/[.+]/', '', $this->base);
		$this->base = $this->base . 'web';
		
		// Find out if we use forward or backslash
		$this->sep = (preg_match('/\\\/', __FILE__)) ? '\\' : '/';
		
		// Get settings
		$this->settings = Plugin::getAllSettings('pgallery');
		
		// Initiate image processing library
		$this->_initlib($this->settings);
		
    }
	
	
	
	
	/**
	  * Function used to initialise the correct image processing library
	  * and to set all of its parameters (imagemagick path, upload dir etc)
	  *
	  * @param	array	$settings		Settings array
	  */
	function _initlib($settings){
		/*$l = new ImageLib;
		$l->base = $this->base;
		$l->sep = $this->sep;
		// $this->lib will now be direct access to the image processing library
		$this->lib =& $l->get_object($settings);*/
		
		// Get settings if they weren't supplied at all or correctly
		if(empty($settings)){
			$settings = Plugin::getAllSettings('pgallery');
		}
		
		switch($settings['lib']){
			case 'gd2':
				$this->imglib = new LibGd();
				$this->imglib->setUploadPath($this->base.$this->sep.$settings['upload_path']);
				return TRUE;
			break;
			
			case 'im':
				$this->imglib = new LibMagick();
				$this->imglib->setUploadPath($this->base.$this->sep.$settings['upload_path']);
				$this->imglib->magick_setPath(stripslashes($settings['im_path']));
				return TRUE;
			break;
			
			default:
				$this->err = 'No library to load!';
				return FALSE;
			break;
		}
	}
	
	
	
	
	/**
	 * Page: Main index page (just show the list of categories for now)
	 */
    public function index(){
        $this->show_categories();
    }
	
	
	
	
	/**
	 * Page: Documentation
	 */
    public function documentation(){
        $this->display('pgallery/views/documentation');
    }
	
	
	
	
	/**
	 * Page: Settings
	 *
	 * 	lib				Library (GD2/ImageMagick)
	 *	im_path			Full system path to ImageMagick library
	 *	upload_path		Relative to base Frog dir (eg. ./public/upload/)
	 *	size_small		Width/height of smallest size (for viewing images in cats)
	 *	size_medium		Width/height of medium size (list of latest images)
	 *	size_large		Width/height of largest size (big enough to view properly)
	 */
    function settings(){
		
		$tmp = Plugin::getAllSettings('pgallery');
		
		$settings = @array(
			'lib' => $tmp['lib'],
			'im_path' => stripslashes($tmp['im_path']),
			'upload_path' => $tmp['upload_path'],
			'size_small' => $tmp['size_small'],
			'size_medium' => $tmp['size_medium'],
			'size_large' => $tmp['size_large'],
		);
		
        $this->display('pgallery/views/settings', array(
			'settings' => $settings,
			'dirs' => $this->_dirlist($this->base, 0, $settings['upload_path']),
		));
		
    }
	
	
	
	
	/**
	 * Save settings
	 */
	function save_settings(){
		
		// Get & format the settings values
		$lib = mysql_escape_string($_POST['lib']);
		$im_path = mysql_escape_string($_POST['im_path']);
		$upload_path = str_replace($this->base, '', $_POST['upload_path']);
		$upload_path = preg_replace('/^[\/|\\\]+/', '', $upload_path);
		$size_small = mysql_escape_string($_POST['size_small']);
		$size_medium = mysql_escape_string($_POST['size_medium']);
		$size_large = mysql_escape_string($_POST['size_large']);
		
		$settings = array(
			'lib' => $lib,
			'im_path' => $im_path,
			'upload_path' => $upload_path,
			'size_small' => $size_small,
			'size_medium' => $size_medium,
			'size_large' => $size_large,
		);
		
		$save = Plugin::setAllSettings($settings, 'pgallery');
		
		if($save){
			
			Flash::set('success', __('The settings have been saved.'));
			
			// Call the test method of the image processing library to see if it works
			$this->_initlib($settings);
			#$this->lib->setUploadPath($settings['upload_path']);
			if($this->imglib->test() === FALSE){
				Flash::set('error', 'Image processing library failed: ' . $this->imglib->msg);
			}
			
		} else {
			
			Flash::set('error', 'An error has occured.');
			
		}
		
		redirect(get_url('plugin/pgallery/settings'));
	}
	
	
	
	
	/**
	 * Page: Show categories
	 */
	public function show_categories(){
		
		// Retrieve categories from the database
		$categories = Record::findAllFrom('Category', '1=1 ORDER BY name ASC');
		
		// Put categories in to array
		$cats = array();
		foreach($categories as $category){
			$cats[$category->id] = $category;
		}
		
		#print_r($cats);
		
		$this->display('pgallery/views/categories', array(
			'html' => $this->_tree($cats),
		));
	}
	
	
	
	
	/**
	 * Page: Add category
	 */
	public function add_category(){
		
		// Check if there is any form data from a previous but unsuccessful submission
		$category = Flash::get('post_data');
		
		// If nothing entered, create new object
        if(empty($category)){
			$category = new Category;
		}
		
		// Get list of all categories in id => name format for cat. selection drop-down
		$cats = $this->_get_categories_dd('id');		
		
		// Show form
		$this->display('pgallery/views/category_addedit', array(
			'action' => 'add',
			'category' => $category,
			'cats' => $cats,
		));
		
	}
	
	
	
	
	/**
	 * Page: Edit category
	 *
	 * @param	$id		ID of the category to edit (URL param)
	 */
	public function edit_category($id){
		
        if(!$category = Category::findByIdFrom('Category', $id)){
            Flash::set('error', __('Category not found!'));
            redirect(get_url('plugin/pgallery'));
        }
		
		// Get list of all categories in id => name format for cat. selection drop-down
		$cats = $this->_get_categories_dd('id');
		
		// Show form
		$this->display('pgallery/views/category_addedit', array(
			'action' => 'edit',
			'category' => $category,
			'cats' => $cats,
		));
		
	}
	
	
	
	
	/**
	 * Page: Save Category (add/edit cat. pages submit here)
	 */
	public function save_category(){
		
		// Make sure we arrive here via a form submission
		if(get_request_method() != 'POST'){
			redirect(get_url('plugin/pgallery'));
		}
		
		
		// Get form data
		$data = $_POST['category'];
		
		// Turn visibility checkbox into tinyint for DB
		$data['visible'] = (isset($data['visible'])) ? 1 : 0;
		// Make URL slug from the name
		$data['slug'] = $this->_makeslug($data['name']);
		
		
		// Set the flash post data to re-populate form
        Flash::set('post_data', (object) $data);
		
		
		// Get URL to redirect to on validation error (add or edit page) or successful update/add
		$url = get_url(
			(empty($data['id'])) 
			? 'plugin/pgallery/add_category' 
			: 'plugin/pgallery/edit_category/'.$data['id']
		);
		
		
		// VALIDATION
		if(empty($data['name'])){
			Flash::set('error', __('You must enter a name for the category.'));
			redirect($url);
		}
		
		// Name <= 50 chars
        if(strlen($data['name']) > 50){
			Flash::set('error', __('The category name must be less than 50 characters.'));
            redirect($url);
        }
		
		
		// Set category object
		$category = new Category($data);
		
		
		// Check result of database function
        if(!$category->save()){
			
			Flash::set('error', __('The category has not been added due to a database error.'));
			
        } else {
			
			$action = (empty($data['id'])) ? 'added' : 'updated';
            Flash::set('success', __('The category has been :actioned.', array(':actioned' => $action)));
			#die(print_r(Record::getQueryLog()));
			
			$url = get_url(
				(empty($data['id']))
				? 'plugin/pgallery'
				: 'plugin/pgallery/images/'.$data['id']
			);
			
        }
		
		#die(print_r(Record::getQueryLog()));
		
		// Finally redirect
		redirect($url);
		
	}
	
	
	
	
	/**
	 * Page: Show images in one category
	 */
	public function images($category_id){
		
		// Redirect to main page if no ID supplied
		if(empty($category_id)){
			Flash::set('error', __('Could not find the supplied category.'));
			redirect(get_url('plugin/pgallery'));
		}
		
		// Get category details
		$category = Category::findByIdFrom('Category', $category_id);
		
		// Get all items where they are in the category that matches the supplied ID
		$images = Record::findAllFrom('Image', 'category_id = ? ORDER BY name ASC', array($category_id));
		
		// Show form
		$this->display('pgallery/views/images', array(
			'category_id' => $category_id,
			'category' => $category,
			'images' => $images,
			'count' => count($images),
            'urlbase' => preg_replace('/\\\/', '/', URL_PUBLIC.'/web/'.$this->settings['upload_path']),
		));
		
	}
	
	
	
	
	/**
	 * Page: add a new image
	 */
	public function add_image($category_id = NULL){
		
		// Check if there is any form data from a previous but unsuccessful submission
		$image = Flash::get('post_data');
		
		// If nothing entered, create new object
        if(empty($image)){
			#echo "No post data, creating obj";
			$image = new Image;
			// If category_id supplied as parameter then set default category ID
			$image->category_id = (!empty($category_id)) ? $category_id : NULL;
		}
		
		// Get list of all categories in id => name format for cat. selection drop-down
		$cats = $this->_get_categories_dd('id');
		
		// Check if we actually have any categories first - can't have homeless images
		if(empty($cats)){
			
			// Can't add image if no categories exit
			Flash::set('error', __('You must create at least one category before you can add an image.'));
			redirect(get_url('plugin/pgallery'));
			
		} else {
			
			// Show form
			$this->display('pgallery/views/image_addedit', array(
				'action' => 'add',
				'image' => $image,
				'cats' => $cats,
			));
			
		}
		
	}
	
	
	
	
	/**
	 * Page: edit image
	 */
	public function edit_image($id){
		
        if(!$image = Image::findByIdFrom('Image', $id)){
            Flash::set('error', __('Image not found!'));
            redirect(get_url('plugin/pgallery'));
        }
		
		// Get list of all categories in id => name format for cat. selection drop-down
		$cats = $this->_get_categories_dd('id');
		
		$this->display('pgallery/views/image_addedit', array(
			'action' => 'edit',
			'image' => $image,
			'cats' => $cats,
		));
		
	}
	
	
	
	
	/**
	 * Save the image
	 */
	public function save_image(){
		
		// Make sure we arrive here via a form submission
		if(get_request_method() != 'POST'){
			redirect(get_url('plugin/pgallery'));
		}
		
		
		// Get form data
		$data = $_POST['image'];
		$file = $_FILES['file'];
		
//		print_r($_POST) . print_r($_FILES);
		
		// Make URL slug from the name
		$data['slug'] = $this->_makeslug($data['name']);
		
		
		// Set the flash post data to re-populate form
		
		#die(print_r($data));
		
        Flash::set('post_data', (object) $data);
		
		// Get URL to redirect to on validation error (add or edit page) or successful update/add
		$url = get_url(
			(empty($data['id'])) 
			? 'plugin/pgallery/add_image' 
			: 'plugin/pgallery/edit_image/'.$data['id']
		);
		
		
		// VALIDATION
		if(empty($data['name'])){
			Flash::set('error', __('You must enter a name for the image.'));
			redirect($url);
		}
		
		// Name <= 50 chars
        if(strlen($data['name']) > 50){
			Flash::set('error', __('The image name must be less than 50 characters.'));
            redirect($url);
        }
		
		// Get settings so we know which sizes to make
		$settings = Plugin::getAllSettings('pgallery');
		
		/*
			Now that the details are assumed valid, we can resize the image, 
			get the resulting filename, and add that to the data array
		*/
		
		
		
		// Resize(original file, desired size (longest), new name, make it square?)

        // Check if a file was actually uploaded
		if(!empty($file['name'])){
            // Get the extension of the original image
            $ext = strtolower(end(explode('.', $file['name'])));
            // Generate new filename
            $basename = uniqid() . "-#." . $ext;

            // Loop through the different sizes and resize
            foreach(array('small', 'medium', 'large') as $size){
                $name = str_replace('#', $size, $basename);
                $resize = $this->imglib->resize($file['tmp_name'], $settings['size_'.$size], $name, ($size == 'small'));
                // If unsuccessful, record error message
                if($resize == FALSE){
                    $fail = $this->imglib->msg;
                }
            }

            // Remove temp file. Does PHP do this automatically though?
            @unlink($file['tmp_name']);

            // Check for upload/resize errors
            if(isset($fail)){
                Flash::set('error', $fail);
                redirect($url);
            }
        }
		
		// Must be OK by here, surely!
		
		// Add the image filename to the data array
		$data['filename'] = $basename;
		
		// Set category cover?
		if(isset($data['cover'])){
			$set_cover = TRUE;
			unset($data['cover']);
		}
		
		// Set image object
		$image = new Image($data);
		
		// Check result of database function
        if(!$image->save()){
			
			Flash::set('error', __('The image has not been added due to a database error.'));
			
        } else {
			
			$action = (empty($data['id'])) ? 'added' : 'updated';
            Flash::set('success', __('The image has been :actioned.', array(':actioned' => $action)));
			
			if($set_cover == TRUE){
				$this->_set_category_cover($image);
			}
			
			#die(print_r(Record::getQueryLog()));
			
			$url = get_url(
				(empty($data['id']))
				? 'plugin/pgallery'
				: 'plugin/pgallery/images/'.$data['category_id']
			);
			
        }
		
		#die(print_r(Record::getQueryLog()));
		
		// Finally redirect
		redirect($url);
		
	}
	
	
	
	
	private function _set_category_cover($image){
		global $__FROG_CONN__;
		$sql = 'UPDATE pgallery_categories SET filename = (SELECT filename FROM pgallery_images WHERE id = ? LIMIT 1) WHERE id = ? LIMIT 1';
		$db = $__FROG_CONN__->prepare($sql);
		$query = $db->execute(array($image->id, $image->category_id));
		return $query;
	}
	
	
	
	
	// .........................
	
	
	
	
	/**
	 * Save the item (form destination of add/edit item pages)
	 */
	/*public function save_image(){
		#print_r($_POST);
		#print_r($_FILES);
		
		global $__FROG_CONN__;
	
		// check if trying to save
		if(get_request_method() != 'POST'){
			redirect(get_url('plugin/pgallery'));
		}
		
		// Create array of fields that will be entered into the database
		$data['item_id'] = mysql_escape_string($_POST['item']['item_id']);
		$data['item_id'] = (!empty($data['item_id'])) ? $data['item_id'] : NULL;
		$data['category_id'] = $_POST['item']['category_id'];
		$data['name'] = mysql_escape_string(preg_replace("(\"|\')", "", $_POST['item']['name']));
		$data['slug'] = $this->_makeslug($data['name']);
		$data['description'] = mysql_escape_string($_POST['item']['description']);
        
		// Set the flash post data to re-populate form
        Flash::set('post_data', (object) $data);
		
		// Validation
		
		// Name <= 50 chars
        if(strlen($data['name']) > 50){
			Flash::set('error', __('The category name must be less than 50 characters'));
            redirect(get_url(($data['item_id'] == NULL) ? 'plugin/pgallery/add_item' : 'plugin/pgallery/edit_item'));
        }
		
		// Finally do database stuff
		
		// If form's item_id is empty, then we are adding a new item
		
		if($data['item_id'] == NULL){
		
			// TODO: call an upload function that uploads/resizes (todo) and then set $data['filename']
			
			// Filename returned from the upload function
			$data['filename'] = 'foobar.jpg';
			
			// Adding
			$sql = "INSERT INTO ".TABLE_PREFIX."pgallery_items 
					(item_id, category_id, name, slug, description, filename) VALUES
					(
						NULL,
						{$data['category_id']},
						'{$data['name']}',
						'{$data['slug']}',
						'{$data['description']}',
						'{$data['filename']}'
					)";
			
			$query = $__FROG_CONN__->prepare($sql);
			$query->execute();
			#die(print_r($query->errorInfo()));
			
			if($query){
				Flash::set('success', __('The item has been added.'));
			} else {
				Flash::set('error', __('The item could not be added due to a database error: ' . $query->errorInfo()));
			}
			
			redirect(get_url('plugin/pgallery'));
			
		} else {
			
			// Updating
			
			$sql = "UPDATE ".TABLE_PREFIX."pgallery_categories SET
					name = '{$data['name']}',
					slug = '{$data['slug']}',
					description = '{$data['description']}',
					visible = {$data['visible']}
					WHERE category_id = {$data['category_id']}
					LIMIT 1";
			
			$query = $__FROG_CONN__->prepare($sql);
			$query->execute();
			#die(print_r($query->errorInfo()));
			
			if($query){
				Flash::set('success', __('The category has been updated.'));
			} else {
				Flash::set('error', __('The category could not be updated due to a database error: ' . $query->errorInfo()));
			}
			
			redirect(get_url('plugin/pgallery'));
			
		}
		
	}*/
	
	
	
	
	/**
	 * Get categories in (id|slug) => name format
	 *
	 * @param	string	Which key to reference the category by (id or slug)
	 * @return	array
	 */
	private function _get_categories_dd($by = 'id'){
		global $__FROG_CONN__;
		
		switch($by){
			case 'id':
				$sql = "SELECT id, name FROM ".TABLE_PREFIX."pgallery_categories";
				$key = 'id';
			break;
			case 'slug':
				$sql = "SELECT slug, name FROM ".TABLE_PREFIX."pgallery_categories";
				$key = 'slug';
			break;
		}
		
		$query = $__FROG_CONN__->prepare($sql);
		$query->execute();
		
		$cats = array();
		
		while($cat = $query->fetchObject()){
			$cats[$cat->$key] = $cat->name;
		}
		
		return $cats;
	}
	
	
	
	
	/**
	 * Make a URL slug, borrowed from CodeIgniter's URL helper.
	 * (Does Frog have it's own function for this?)
	 *
	 * @param	string	String to convert into a slug
	 * @return	string	Converted string
	 */
	private function _makeslug($str){
		$sep = '-';
		
		// Regular expressions of characters we don't want
		$trans = array(
						'&\#\d+?;'			=> '',
						'&\S+?;'			=> '',
						'\s+'				=> $sep,
						'[^a-z0-9\-\._]'	=> '',
						$sep.'+'			=> $sep,
						$sep.'$'			=> $sep,
						'^'.$sep			=> $sep
					  );
		
		$str = strip_tags($str);
		
		foreach ($trans as $key => $val){
			$str = preg_replace("#".$key."#i", $val, $str);
		}
		
		// Convert to lower case, trim etc
		return strtolower(trim(stripslashes($str)));
	}
	
	
	
	
	/**
	 * Recursive function used to generate a tree of categories
	 * Credit: http://stackoverflow.com/questions/333735/how-to-build-a-tree-view-with-php-sql
	 *
	 * @param	[various]
	 * @return	html tree of UL and LI tags
	 */
	private function _tree($array, $parent = 0, $level = 0){
		
		$html = '';
		$has_children = false;
		
		foreach($array as $key => $obj){
			
			if ($obj->parent_id == $parent){                   
				
				if ($has_children === false){
					$has_children = true;  
					$html .= '<ul id="side-map-root" class="level-' . $level . '">'."\n";
					$level++;
				}
				
				$html .= sprintf('<li class="node"><a href="%s">%s</a>',
					get_url('plugin/pgallery/images/'.$obj->id),
					$obj->name
				);
				
				$html .= $this->_tree($array, $key, $level);
				
				$html .= "</li>\n";
				
			}
			
		}
		
		if ($has_children === true){
			$html .= "</ul>\n";
		}
		
		return $html;
		
	}
	
	
	
	
	/**
	 * Recursive directory list
	 */
	function _dirlist($path = '.', $level = 0, $selected = ''){
		
		$html = '';
		
		// Don't include any of these folders in the final list
		$ignore = array('cgi-bin', '.', '..');
		
		// Open handle to base directory
		$dh = @opendir($path);
		
		while(false !== ($file = readdir($dh))){
			
			if(!in_array($file, $ignore)){
				
				$spaces = str_repeat('-', ($level * 2));
				$thispath = "$path{$this->sep}$file";
				$s = ("{$this->base}{$this->sep}$selected" == $thispath) ? 'selected="selected"' : '';
				
				if(is_dir($thispath)){
					
					$html .= "<option value=\"$thispath\" $s>$spaces $file</option>";
					$html .= $this->_dirlist("$thispath", ($level+1), $selected);
					
				}
				
			}
			
		}
		
		closedir($dh);
		
		return $html;
		
	}
	
	
	
	
}

/* End of file: pgallery/PGalleryController.php */